﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Management.Automation;
using Contracts;
using Newtonsoft.Json;
using RestSharp;
using static InspectorCmdlet.InspectorRestClient;

namespace InspectorCmdlet
{
  #region Base Classes
  
  /// <summary>
  /// Base class for all Cmdlets except for Start-Session
  /// Parameters: AuthToken (t) - Required
  ///             Environment (e) - Not Required (Default: FR)
  /// </summary>
  public abstract class InspectorCmdletBase : PSCmdlet
  {
    [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = "The authentication token retrieved by the Start-InspectorSession command")]
    [Alias("t")]
    public Guid AuthToken { get; set; }

    [Parameter(ValueFromPipelineByPropertyName = true, HelpMessage = "The Ascribe environment to run the command against. If not specified, will assume FR.")]
    [Alias("e")]
    public Environments Environment { get; set; }

  }

  /// <summary>
  /// Base class for all Submit-* Cmdlets
  /// Parameters: InspectionKey (key): Inspection Key to process
  ///             ContinueOnError
  /// </summary>
  public abstract class InspectionJobBase : InspectorCmdletBase
  {
    #region Parameters
    [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true)]
    [Alias("key")]
    public int InspectionKey { get; set; }

    [Parameter(HelpMessage = "If specified, the job will continue execution if a task reports errors. If omitted, the job will stop when any task reports errors")]
    public SwitchParameter ContinueOnError { get; set; }

    [Parameter(HelpMessage = "If specified, the job will not start, but will report any errors encountered when validating the request. If omitted the job will start if no validation errors are reported")]
    public SwitchParameter ValidateOnly { get; set; }

    #endregion
    /// <summary>
    /// Returns a question key in the current inspection given the Question ID. Returns null if not found.
    /// </summary>
    /// <param name="QuestionID">Question ID</param>
    /// <returns>Question Key (int) or null</returns>
    protected int? GetQuestionKey(string QuestionID, RestClient client)
    {
      WriteVerbose("Looking up ID of Question: " + QuestionID);

      int? questionKey = null;
      // request to look up all questions for the current inspection
      var inspectionRequest = InspectorRestClient.GetClientRequest(AuthToken, Resources.Inspections, Method.GET, null, InspectionKey.ToString());
      var response = client.Execute<InspectionResponse>(inspectionRequest);
      if (response.Data.errors == null)
      {
        // find question given Question ID
        InspectionDetails inspection = response.Data.inspection;
        var question = inspection.questions.Values.First(q => q.id.Equals(QuestionID, StringComparison.CurrentCultureIgnoreCase));
        if (question != null)
        {
          // question located - return the key
          questionKey = question.key;
        }
      }
      return questionKey;
    }

    /// <summary>
    /// Checks the requested job's status every 2 seconds, using WriteProgress to display the job's progress. Once the job is complete, will return an InspectionJobStatus object of the completed Job.
    /// </summary>
    /// <param name="jobResponse">The jobResponse of the job to monitor</param>
    /// <returns>InspectionJobStatus object</returns>
    protected InspectionJobStatus MonitorJob(JobResponse jobResponse, RestClient client)
    {
      if (jobResponse.jobGuid == Guid.Empty)
      {
        WriteObject(Newtonsoft.Json.JsonConvert.SerializeObject(jobResponse, Formatting.Indented));
        return null;
      }
      Stopwatch timer = new Stopwatch();
      timer.Start();

      // sleep 2 seconds to start as the job doesn't update on the server right away
      System.Threading.Thread.Sleep(2000);
      var JobGuid = jobResponse.jobGuid;

      // retrieve the job
      var request = GetClientRequest(this.AuthToken, Resources.Jobs, Method.GET, null, JobGuid.ToString());
      var response = client.Execute<JobStatusResponse>(request);
      var jobStatus = response.Data;
      var job = jobStatus.job;

      // continue showing status until End Date contains a value
      while (!job.endDateUtc.HasValue)
      {
        var status = job.status;
        if (status.fileLoads != null && !status.fileLoads.Any(l => l.endDateUtc.HasValue))
        {
          var loadTask = status.fileLoads.First(l => !l.endDateUtc.HasValue);
          if (loadTask != null)
          {
            WriteTaskProgress("Loading Responses...", timer, loadTask);
          }
        }
        else if (status.generateAnswers != null)
        {
          if (!status.generateAnswers.endDateUtc.HasValue)
          {
            WriteTaskProgress("Generating Answers...", timer, status.generateAnswers);
          }
        }
        else if (status.newAnalyses != null && !status.newAnalyses.Any(l => l.endDateUtc.HasValue))
        {
          var analysisTask = status.newAnalyses.First(l => !l.endDateUtc.HasValue);
          if (analysisTask != null)
          {
            WriteTaskProgress("Running new analysis...", timer, analysisTask);
          }
        }
        else if (status.newExports != null && !status.newExports.Any(l => l.endDateUtc.HasValue))
        {
          var exportTask = status.newExports.First(l => !l.endDateUtc.HasValue);
          if (exportTask != null)
          {
            WriteTaskProgress("Running new export...", timer, exportTask);
          }
        }
        else if (status.translations != null && status.translations.Any(l => l.endDateUtc.HasValue))
        {
          var translationTask = status.translations.First(l => !l.endDateUtc.HasValue);
          if (translationTask != null)
          {
            WriteTaskProgress("Translating...", timer, translationTask); ; ;
          }
        }

        // sleep 2 seconds then retrieve job again & loop
        System.Threading.Thread.Sleep(2000);
        response = client.Execute<JobStatusResponse>(request);
        job = response.Data.job;
      }

      // job is complete, return the current status to the calling function
      WriteVerbose(JsonConvert.SerializeObject(job.status, Formatting.Indented));
      return job.status;
    }

    /// <summary>
    /// Writes the task progress to the console window using the WriteProgress method
    /// </summary>
    /// <param name="Description">Description of the running task</param>
    /// <param name="timer">The Stopwatch object that is keeping the running time</param>
    /// <param name="task">The task object that contains phase/percent complete</param>
    protected void WriteTaskProgress(string Description, Stopwatch timer, TaskStatusBase task)
    {
      string progressString = "";
      string percentComplete = "";
      if (task.percentComplete > 0)
      {
        percentComplete = string.Format(" [{0}%]", Math.Ceiling(task.percentComplete));
      }
      progressString = string.Format("Phase {0}/{1}{2} [{3}:{4}]", task.currentPhase, task.maxPhase, percentComplete, Math.Floor(timer.Elapsed.TotalMinutes), timer.Elapsed.Seconds.ToString().PadLeft(2, '0'));
      WriteProgress(new ProgressRecord(0, Description, progressString));
    }
  }
  #endregion

  #region Start-InspectorSession
  /// <summary>
  /// Uses a POST call to the Sessions API to create a new Ascribe user session, returning an object containing the Authentication Token and the current environment
  /// </summary>
  [Cmdlet(VerbsLifecycle.Start, "InspectorSession", HelpUri = "http://fr.test.languagelogic.net/inspector/Help/Api/POST-Sessions")]
  public class GetTokenCommand : PSCmdlet
  {
    #region Paramaters
    [Parameter(Mandatory = true, HelpMessage = "The id of the Ascribe account. This is the id entered in the Account section of the logon page in Ascribe.")]
    [Alias("a")]
    public string Account { get; set; }

    [Parameter(Mandatory = true, HelpMessage = "The logon name of the user. The created session will have this user associated with it. Any Inspections created during this session will have this user as the owner of the new Inspection.")]
    [Alias("u")]
    public string Username { get; set; }

    [Parameter(Mandatory = true, HelpMessage = "The user's password.")]
    [Alias("p")]
    public string Password { get; set; }

    [Parameter(HelpMessage = "The Ascribe environment to run the command against. If not specified, will assume FR.")]
    [Alias("e")]
    public Environments Environment { get; set; }
    #endregion

    private RestClient client;

    protected override void BeginProcessing()
    {
      client = InspectorRestClient.GetClient(Environment);
    }
    protected override void ProcessRecord()
    {
      // build a NewSessionRequest and submit to the API
      NewSessionRequest req = new NewSessionRequest() { account = Account, password = Password, userName = Username };
      var RestRequest = InspectorRestClient.GetClientRequest(Guid.Empty, Resources.Sessions, Method.POST, req);
      var response = client.Execute<NewSessionResponse>(RestRequest);
      WriteVerbose(JsonConvert.SerializeObject(response.Data, Formatting.Indented));
      if (response.Data.errors != null && response.Data.errors.Length > 0)
      {
        throw new Exception(string.Join("; ", response.Data.errors));
      }
      
      // write an object with AuthenticationToken and Environment to the output stream
      WriteObject(new TokenResponse(new Guid(response.Data.authenticationToken), Environment));
    }

    protected override void EndProcessing()
    {
      client = null;
    }
  }
  #endregion

  #region Get-InspectorResource

  /// <summary>
  /// Using GET method on various API resources to retrieve data and output the raw JSON response returned by the API.
  /// </summary>
  [Cmdlet(VerbsCommon.Get, "InspectorResource", HelpUri = "http://fr.test.languagelogic.net/inspector/Help")]
  public class GetResource : InspectorCmdletBase
  {
    #region Parameters
    [Parameter(Mandatory = true, Position = 0, HelpMessage = "The resource to query for data {Exports | Inspections | Jobs | Languages | Processors | RuleSets | Sessions | Taxonomies | Users}")]
    [Alias("r")]
    public Resources Resource { get; set; }

    [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true, HelpMessage = "ID/JobGuid/Key of resource to retrieve")]
    [Alias("i", "JobGuid", "key")]
    public string ID { get; set; }

    [Parameter(HelpMessage = "Only used when Resource == \"Jobs\" -- Retrieves list of running jobs")]
    public SwitchParameter Running { get; set; }

    #endregion

    protected override void ProcessRecord()
    {
      base.ProcessRecord();
      RestClient client = GetClient(Environment);

      if (Resource == Resources.Jobs && Running) ID = "Running";
      var request = InspectorRestClient.GetClientRequest(AuthToken, Resource, Method.GET, null, ID);
      var response = client.Execute(request);
      var obj = JsonConvert.DeserializeObject(response.Content);
      string s = response.Content;
      if (obj != null)
        s = JsonConvert.SerializeObject(obj, Formatting.Indented);
      else
        s = response.ErrorMessage;

      WriteObject(s);
    }

  }
  #endregion

  #region Add-Inspection
  /// <summary>
  /// Uses POST to Inspections resource to create a new inspection 
  /// </summary>
  [Cmdlet(VerbsCommon.Add, "Inspection", HelpUri = "http://fr.test.languagelogic.net/inspector/Help/Api/POST-Inspections")]
  public class AddInspection : InspectorCmdletBase
  {
    #region Parameters
    [Parameter(Mandatory = false, HelpMessage = "The desired id of the new Inspection. The specified value will be trimmed of leading and trailing whitespace. The resulting string must be unique among all Inspections by case insensitive comparison. Because the id's of deleted inspections cannot be reused, it is possible for a request to create a new Inspection to fail because the specified id is the same as that of a deleted Inspection. In this case the errors property of the response will be non-null, and the duplicateId property of the response will be true.")]
    [Alias("id")]
    public string InspectionID { get; set; }

    [Parameter(HelpMessage = "A description of the new Inspection")]
    [Alias("d")]
    public string Description { get; set; }

    [Parameter(HelpMessage = "If omitted the new Inspection will be visible only to the owner (the user for the current session). If specified the new Inspection will be visible in read-only form to all users.")]
    [Alias("public", "p")]
    public SwitchParameter IsPublic { get; set; }
    
    [Parameter(HelpMessage = "If specified, a link to this Inspection may be distributed to others, who may use the link to view the Inspection in read-only form. If omitted no access to this Inspection is permitted via a link.")]
    [Alias("l", "link")]
    public SwitchParameter AllowLink { get; set; }

    [Parameter(HelpMessage = "If omitted other users who can see this Inspection can change its ownership. Only the owner of the Inspection can modify it. If specified, other users with permission to see this Inspection are prevented from changing the ownership of the Inspection. Users with Administrator privilege can change ownership of any Inspection, regardless of this value.")]
    [Alias("pto")]
    public SwitchParameter PreventTakingOwnership { get; set; }

    #endregion


    protected override void ProcessRecord()
    {
      RestClient client = GetClient(Environment);

      var addrequest = new NewInspectionRequest()
      {
        id = this.InspectionID,
        allowLink = this.AllowLink,
        description = this.Description,
        isPublic = this.IsPublic,
        preventTakingOwnership = this.PreventTakingOwnership
      };

      // POST the AddRequest to the API
      var request = InspectorRestClient.GetClientRequest(AuthToken, Resources.Inspections, Method.POST, addrequest);
      var response = client.ExecuteDynamic(request);
      WriteVerbose(JsonConvert.SerializeObject(response.Data, Formatting.Indented));
      if (response.Data.duplicateId)
      {
        throw new Exception("Inspection ID already exists.");
      }
      else if (response.Data.errors != null && response.Data.errors.Count > 0)
      {
        throw new Exception(string.Join("; ", response.Data.errors));
      }
      int inspectionKey = (int)response.Data.inspectionKey;
      AddInspectionResponse o = new AddInspectionResponse(inspectionKey, AuthToken, Environment);
      WriteObject(o);
    }


  }
  #endregion

  #region Submit-InspectionFileLoad
  /// <summary>
  /// Uses POST to Jobs method to create a new Job that loads data from a specific (S)FTP file to the specified inspection
  /// </summary>
  [Cmdlet(VerbsLifecycle.Submit, "InspectionFileLoad", HelpUri = "http://fr.test.languagelogic.net/inspector/Help/ResourceModel?modelName=FileLoadTaskRequest")]
  public class FileLoadJob : InspectionJobBase
  {
    #region Parameters

    [Parameter(Mandatory = true, HelpMessage = "The uri of the file to load. This must be an absolute uri, with ftp or sftp scheme")]
    public string Uri { get; set; }

    [Parameter(Mandatory = true, HelpMessage = "The username to use when logging on to the ftp site that hosts the uri")]
    [Alias("user", "u", "username")]
    public string FtpUsername { get; set; }

    [Parameter(Mandatory = true, HelpMessage = "The password to use when logging on to the ftp site that hosts the uri")]
    [Alias("password", "pass", "p")]
    public string FtpPassword { get; set; }

    [Parameter(HelpMessage = "The key of the Rule Set to use when loading responses from the file.")]
    [Alias("rs")]
    public int? RuleSetKey { get; set; }

    #endregion

    protected override void ProcessRecord()
    {
      RestClient client = GetClient(Environment);

      var fileLoadRequest = new FileLoadTaskRequest()
      {
        uri = this.Uri,
        ftpPassword = this.FtpPassword,
        ftpUserName = this.FtpUsername,
        ruleSetKey = this.RuleSetKey
      };
      var restRequest = new InspectionJobRequest()
      {
        continueOnError = this.ContinueOnError,
        inspectionKey = this.InspectionKey,
        validateOnly = this.ValidateOnly,
        fileLoads = new List<FileLoadTaskRequest>(new FileLoadTaskRequest[] { fileLoadRequest })
      };
      WriteVerbose(JsonConvert.SerializeObject(restRequest, Formatting.Indented));
      var request = InspectorRestClient.GetClientRequest(AuthToken, Resources.Jobs, Method.POST, restRequest);
      var response = client.Execute<JobResponse>(request);
      WriteVerbose(JsonConvert.SerializeObject(response.Data, Formatting.Indented));
      if (response.Data.errors != null && response.Data.errors.Length > 0)
      {
        throw new Exception(string.Join("; ", response.Data.errors));
      }

      MonitorJob(response.Data, client);

      SubmitJobResponse o = new SubmitJobResponse(InspectionKey, response.Data.jobGuid, AuthToken, Environment);

      WriteObject(o);
    }
  }
  #endregion

  #region Submit-InspectionGenerateAnswers
  /// <summary>
  /// Uses POST to Jobs method to create a new Job that generates answers for specified question(s)
  /// </summary>
  [Cmdlet(VerbsLifecycle.Submit, "InspectionGenerateAnswers", HelpUri = "http://fr.test.languagelogic.net/inspector/Help/ResourceModel?modelName=GenerateAnswersRequest")]
  public class GenerateAnswersJob : InspectionJobBase
  {
    #region Parameters
    [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = "A comma-delimited list of keys and/or IDs of the questions for which to generate answers.")]
    [Alias("id", "ids", "qkey", "qkeys")]
    public string Questions { get; set; }
    #endregion

    protected override void ProcessRecord()
    {
      RestClient client = GetClient(Environment);

      List<int> questionKeys = new List<int>();
      string[] questionArr = Questions.Split(',');
      foreach (string s in questionArr)
      {
        // If the current item in the array is not numeric, attempt to look up the key using the string as the ID.
        string id = s.Trim();
        int key;
        if (!int.TryParse(id, out key))
        {
          var result = GetQuestionKey(id, client);
          if (result.HasValue)
            key = result.Value;
          else
            key = -1;
        }
        if (key > -1) questionKeys.Add(key);
      }
      if (questionKeys.Count > 0)
      {
        WriteVerbose("Generating Answers for Question Key(s): " + string.Join(", ", questionKeys.ToString()));
        var generateAnswersRequest = new GenerateAnswersTaskRequest()
        {
          questionKeys = questionKeys
        };
        var restRequest = new InspectionJobRequest()
        {
          continueOnError = this.ContinueOnError,
          inspectionKey = this.InspectionKey,
          validateOnly = this.ValidateOnly,
          generateAnswers = generateAnswersRequest
        };
        WriteVerbose(JsonConvert.SerializeObject(restRequest, Formatting.Indented));
        var request = InspectorRestClient.GetClientRequest(AuthToken, Resources.Jobs, Method.POST, restRequest);
        var response = client.Execute<JobResponse>(request);
        WriteVerbose(JsonConvert.SerializeObject(response.Data, Formatting.Indented));
        if (response.Data.errors != null && response.Data.errors.Length > 0)
        {
          throw new Exception(string.Join("; ", response.Data.errors));
        }
        var finalTaskStatus = MonitorJob(response.Data, client);

        SubmitJobResponse o = new SubmitJobResponse(InspectionKey, response.Data.jobGuid, AuthToken, Environment);

        WriteObject(o);
      }
    }
  }
  #endregion

  #region Submit-InspectionNewAnalysis
  /// <summary>
  /// Uses POST to Jobs method to create a new Job that runs a new analysis for the requested question(s)
  /// </summary>
  [Cmdlet(VerbsLifecycle.Submit, "InspectionNewAnalysis", HelpUri = "http://fr.test.languagelogic.net/inspector/Help/ResourceModel?modelName=NewAnalysisTaskRequest")]
  public class NewAnalysisJob : InspectionJobBase
  {
    #region Parameters
    [Parameter(Mandatory = true, HelpMessage = "A comma-delimited list of keys and/or IDs of the questions to include in the analysis. At least one question must be specified.")]
    [Alias("id", "ids", "qkey", "qkeys")]
    public string Questions { get; set; }


    [Parameter(HelpMessage = "The processor for the analysis. If specified, must be one of the processor ids returned by querying the Processors resource.")]
    [Alias("proc")]
    public string ProcessorID { get; set; }
    
    [Parameter(HelpMessage = "The key of the rule set to apply to the analysis. If specified, must be one of the processor keys returned by querying the RuleSets resource.")]
    [Alias("rs")]
    public int? RuleSetKey { get; set; }

    [Parameter(HelpMessage = "The key of the taxonomy to use to group Topics. If specified, must be one of the taxonomy keys returned by querying the Taxonomies resource. If not specified Topics will be grouped automatically.")]
    [Alias("tax")]
    public int? TaxonomyKey { get; set; }

    #endregion

    protected override void ProcessRecord()
    {
      RestClient client = GetClient(Environment);

      List<int> questionKeys = new List<int>();
      string[] questionArr = Questions.Split(',');
      foreach (string s in questionArr)
      {
        string id = s.Trim();
        int key;
        if (!int.TryParse(id, out key))
        {
          int? result = GetQuestionKey(id, client);
          if (result.HasValue)
            key = result.Value;
          else
            key = -1;
        }
        if (key > -1) questionKeys.Add(key);
      }

      NewAnalysisTaskRequest task = new NewAnalysisTaskRequest()
      {
        questionKeys = questionKeys,
        processorId = ProcessorID,
        ruleSetKey = RuleSetKey,
        taxonomyKey = TaxonomyKey
      };

      var restRequest = new InspectionJobRequest()
      {
        continueOnError = this.ContinueOnError,
        inspectionKey = this.InspectionKey,
        validateOnly = this.ValidateOnly,
        newAnalyses = new List<NewAnalysisTaskRequest>(new NewAnalysisTaskRequest[] { task })
      };
      WriteVerbose(JsonConvert.SerializeObject(restRequest, Formatting.Indented));
      var request = InspectorRestClient.GetClientRequest(AuthToken, Resources.Jobs, Method.POST, restRequest);
      var response = client.Execute<JobResponse>(request);
      WriteVerbose(JsonConvert.SerializeObject(response.Data, Formatting.Indented));
      if (response.Data.errors != null && response.Data.errors.Length > 0)
      {
        throw new Exception(string.Join("; ", response.Data.errors));
      }
      var finalTaskStatus = MonitorJob(response.Data, client);

      AnalysisResponse o = new AnalysisResponse(InspectionKey, response.Data.jobGuid, AuthToken, Environment, 0);

      WriteObject(o);
    }
  }
  #endregion

  #region Submit-InspectionTranslation
  /// <summary>
  /// Uses POST to Jobs method to create a new Job that translates responses from one question into a new or existing question
  /// </summary>
  [Cmdlet(VerbsLifecycle.Submit, "InspectionTranslation", HelpUri = "http://fr.test.languagelogic.net/inspector/Help/ResourceModel?modelName=TranslationTaskRequest")]
  public class TranslationJob : InspectionJobBase
  {
    #region Parameters
    [Parameter(Mandatory = true, HelpMessage = "The key or ID of the question containing responses to translate.")]
    [Alias("sq")]
    public string SourceQuestion { get; set; }

    [Parameter(Mandatory = true, HelpMessage = "The key or ID for the question into which the translated responses will be placed. The value provided will be trimmed of leading and trailing white space. If the question does not exist it will be created. If the question does exist the translated responses will be added to the existing responses in the question, but only if targetQuestionMayExist is true.")]
    [Alias("tq")]
    public string TargetQuestion { get; set; }

    [Parameter(HelpMessage = "If omitted validation will fail if a question exists with targetQuestionId. If true and the target question exists the translated responses will be added to the existing responses in the question.")]
    public SwitchParameter TargetQuestionMayExist { get; set; }

    [Parameter(Mandatory = true, HelpMessage = "The identifier for the language to translate to. This must be one of the language identifiers returned by the Languages resource.")]
    [Alias("tl")]
    public string TargetLanguageID { get; set; }

    [Parameter(HelpMessage = "If not specified language detection will be used to determine the language of a response to be translated. If specified, all responses in the source question are taken to be this language, one of the identifers returned by the Languages resource.")]
    [Alias("sl")]
    public string SourceLanguageID { get; set; }

    [Parameter(HelpMessage = "This property is ignored if sourceLanguageId is specified. If not specified all responses from the source question will be submitted for translation. If specified, language detection will be used to determine the language of a response to be translated. If the detected language matches the targetLanguageId, this response will be copyed unmodified to the target question. This avoids a translation fee for responses already in the target language, at the risk of improperly detecting the source language. Note that language detection occurs at two points in the translation process, language detection used to bypass the use of the translation engine, and language detection performed by the translation engine. Language detection goverened by this property is used only to bypass the use of the translation engine to avoid translation fees. The final determination of the source language of a translated response is performed by the translation engine.")]
    public SwitchParameter UseLanguageDetection { get; set; }

    [Parameter(HelpMessage = "An Inspection may have a question with id = RLanguage. If so, the responses to this question specify the language of the responses from that respondent, but only if the response to the RLanguage question is one of the language identifiers returned by the Languages resource. If this is not specified, the RLanguage question is not used. If this is specified, the response to the RLanguage question (if a valid language id) is used as the source language for a response. If both this property and the useLanguageDetection property are true, then the language specified by the RLanguage question is validated by language detection. If the results are in agreement, the response is submitted to the translation engine specifying that the source language is this language. Otherwise, the response is submitted to the translation engine with a request for automatic language detection by the translation engine.")]
    [Alias("useRQ")]
    public SwitchParameter UseRLanguageQuestion { get; set; }
    #endregion

    protected override void ProcessRecord()
    {
      RestClient client = GetClient(Environment);

      int qkey = 0;
      if (!int.TryParse(SourceQuestion, out qkey))
      {
        int? result = GetQuestionKey(SourceQuestion, client);
        if (result.HasValue)
          qkey = result.Value;
        else
          qkey = -1;
      }
      if (qkey >= 0)
      {
        WriteVerbose(string.Format("Translating Question {0}", qkey));
        var tReq = new TranslationTaskRequest()
        {
          sourceLanguageId = SourceLanguageID,
          sourceQuestionKey = qkey,
          targetLanguageId = TargetLanguageID,
          targetQuestionId = TargetQuestion,
          targetQuestionMayExist = TargetQuestionMayExist,
          useLanguageDetection = UseLanguageDetection,
          useRLanguageQuestion = UseRLanguageQuestion
        };
        var request = new InspectionJobRequest()
        {
          continueOnError = this.ContinueOnError,
          inspectionKey = this.InspectionKey,
          validateOnly = this.ValidateOnly,
          translations = new List<TranslationTaskRequest>(new TranslationTaskRequest[] { tReq })
        };
        WriteVerbose(JsonConvert.SerializeObject(request, Formatting.Indented));
        var clientRequest = InspectorRestClient.GetClientRequest(AuthToken, Resources.Jobs, Method.POST, request);
        var response = client.Execute<JobResponse>(clientRequest);
        WriteVerbose(JsonConvert.SerializeObject(response.Data, Formatting.Indented));

        if (response.Data.errors != null && response.Data.errors.Length > 0)
        {
          throw new Exception(string.Join("; ", response.Data.errors));
        }
        var finalTaskStatus = MonitorJob(response.Data, client);

        SubmitJobResponse o = new SubmitJobResponse(InspectionKey, response.Data.jobGuid, AuthToken, Environment);

        WriteObject(o);
      }
    }
  }
  #endregion

  #region Submit-InspectionUpdateAnalyses
  /// <summary>
  /// Uses POST to Jobs method to create a new Job that updates one or more existing analyses
  /// </summary>
  [Cmdlet(VerbsLifecycle.Submit, "InspectionUpdateAnalyses", HelpUri = "http://fr.test.languagelogic.net/inspector/Help/ResourceModel?modelName=UpdateAnalysesTaskRequest")]
  public class UpdateAnalysesJob : InspectionJobBase
  {
    #region Parameters
    [Parameter(HelpMessage = "If specified, all analyses in the Inspection will be updated, and the analysisKeys property will be ignored. If not specified, the analyses specified by the analysisKeys property will be updated.")]
    [Alias("all")]
    public SwitchParameter UpdateAll { get; set; }

    [Parameter(HelpMessage = "Required if updateAll is false. A non-empty comma-delimited list of analysis keys specifying the analyses to update.")]
    [Alias]
    public string AnalysisKeys { get; set; }

    #endregion

    protected override void ProcessRecord()
    {
      RestClient client = GetClient(Environment);

      WriteVerbose("Updating Analyses...");
      List<int> analysisKeys = new List<int>();
      if (!string.IsNullOrEmpty(AnalysisKeys))
      {
        AnalysisKeys.Split(',').ToList().ForEach(k => analysisKeys.Add(int.Parse(k.Trim())));
      }
      var uReq = new UpdateAnalysesTaskRequest()
      {
        analysisKeys = analysisKeys,
        updateAll = UpdateAll
      };
      var request = new InspectionJobRequest()
      {
        continueOnError = this.ContinueOnError,
        inspectionKey = this.InspectionKey,
        validateOnly = this.ValidateOnly,
        updateAnalyses = uReq
      };
      WriteVerbose(JsonConvert.SerializeObject(request, Formatting.Indented));
      var clientRequest = InspectorRestClient.GetClientRequest(AuthToken, Resources.Jobs, Method.POST, request);
      var response = client.Execute<JobResponse>(clientRequest);
      WriteVerbose(JsonConvert.SerializeObject(response.Data, Formatting.Indented));

      if (response.Data.errors != null && response.Data.errors.Length > 0)
      {
        throw new Exception(string.Join("; ", response.Data.errors));
      }
      var finalTaskStatus = MonitorJob(response.Data, client);

      SubmitJobResponse o = new SubmitJobResponse(InspectionKey, response.Data.jobGuid, AuthToken, Environment);

      WriteObject(o);
    }
  }
  #endregion

  #region Submit-InspectionNewExport
  /// <summary>
  /// Uses POST to Jobs method to create a new Job that exports data from an inspection
  /// </summary>
  [Cmdlet(VerbsLifecycle.Submit, "InspectionNewExport", HelpUri = "http://fr.test.languagelogic.net/inspector/Help/ResourceModel?modelName=NewExportTaskRequest")]
  public class NewExportJob : InspectionJobBase
  {
    #region Parameters
    [Parameter(Mandatory = true, HelpMessage = "The desired export type, which must be one of the export type ids returned by querying the Exports resource.")]
    public string ExportTypeID { get; set; }
    
    [Parameter(Mandatory = true, HelpMessage = "The file name of the export, which must be a valid file name with no path. The extension associated with the specified export type will be appended to this file name, so no extension is needed for this file name. If the extension associated with the export is .xlsx, and you specify a file name of 'foo.csv', the resulting file name will be 'foo.csv.xlsx'")]
    public string ExportFileName { get; set; }
    
    [Parameter(HelpMessage = "Required if the specified export type requires an analysis key. The key of the analysis to export.")]
    public int? AnalysisKey { get; set; }
    
    [Parameter(HelpMessage = "If the export type requires an analysis key and this value is true then it will ignore the analysisKey parameter and run the export for all valid analyses, putting the resulting files in a ZIP archive.")]
    [Alias("all")]
    public SwitchParameter ExportAllAnalyses { get; set; }
    #endregion

    protected override void ProcessRecord()
    {
      RestClient client = GetClient(Environment);

      WriteVerbose("Updating Analyses...");

      var eReq = new NewExportTaskRequest()
      {
        exportTypeId = ExportTypeID,
        fileName = ExportFileName,
        analysisKey = AnalysisKey,
        exportAllAnalyses = ExportAllAnalyses
      };
      var request = new InspectionJobRequest()
      {
        continueOnError = this.ContinueOnError,
        inspectionKey = this.InspectionKey,
        validateOnly = this.ValidateOnly,
        newExports = new List<NewExportTaskRequest>(new NewExportTaskRequest[] { eReq })
      };
      WriteVerbose(JsonConvert.SerializeObject(request, Formatting.Indented));
      var clientRequest = InspectorRestClient.GetClientRequest(AuthToken, Resources.Jobs, Method.POST, request);
      var response = client.Execute<JobResponse>(clientRequest);
      WriteVerbose(JsonConvert.SerializeObject(response.Data, Formatting.Indented));

      if (response.Data.errors != null && response.Data.errors.Length > 0)
      {
        throw new Exception(string.Join("; ", response.Data.errors));
      }
      var finalTaskStatus = MonitorJob(response.Data, client);

      NewExportJobResponse o = new NewExportJobResponse(InspectionKey, response.Data.jobGuid, AuthToken, Environment, finalTaskStatus.newExports.First().uri);

      WriteObject(o);
    }
  }

  #endregion

  
}
